#include "core_pch.h"
#include "RTTI.h"
#include "RawBuf.h"
#include "Res.h"

TArray< EventDef*> EventDef::sEventArray;
EventArgDef::EventArgDef( const char* name,  EDataType dataType)
:Name( name)
,DataType( dataType)
,Next(0)
{
}


EventDef::EventDef( const char* name)
:Name( name)
,ID( null_index)
,ArgList( 0)
{
}


////////////////////////////


FuncArgDef::FuncArgDef( const char* name, EDataType type)
:DataType(type) ,Name(name)
,Next(0)
,IsOut( false)
,HasDefaultVal( false)
{
}

FuncBind::FuncBind( const char* name, ObjFunc_Bind bindFunc)
:Index( null_index)
,Name( name)
,Bind_ObjFunc_Adp( bindFunc)
,ArgList( 0)
{
}


////////////////////////////////////////////

EnumValue::EnumValue( const char* name, s32_t val)
:Name( name)
,Value( val)
,Next( 0)
{
}


EnumDef::EnumDef( const char* name)
:Name( name)
,ItemList( 0)
{
}


///////////////////PropDef///////////////////
PropDef::PropDef( const char* name, EDataType type, s32_t pos)
:Name( name)
,Pos( pos)
,ClassType( 0)
,EditorReadOnly( false)
,Editable( true)
,IsArray( false)
,IsOwnerLink( false)
,KismetVar( true)
,IsSaveToDisk( true)
,Type( type)
,Index( null_index)
,OwnerCls(0)
,OwnerStruct(0)
{
}

///////////////////PropData///////////////////
void PropData::CopyTo( PropData& dstData)
{
	_check(Def->Type != dstData.Def->Type);
	if( !Def->IsArray)
	{
		if( Data_SString == Def->Type)
		{
			*(SString*)dstData.Data = *(SString*)Data;
		}
		else if( Data_DString == Def->Type)
		{
			*(DString*)dstData.Data = *(DString*)Data;
		}
		else if( Data_Struct_Ins == Def->Type)
		{
			Def->StructType->CopyStruct( dstData.Data, Data);
		}
		else if( Data_RefObj_Ptr == Def->Type)
		{
			if( Def->ClassType->IsTypeOf( Res::RTTI()))
			{
				(*(P( Obj)*)dstData.Data) = (*(P( Obj)*)Data);
			}
			else
			{
				if( (*(P( Obj)*)Data))
				{
					(*(P( Obj)*)dstData.Data) = (*(P( Obj)*)Data)->Clone();
				}
			}
		}
		else
		{
			//raw data
			s32_t itemSize = RawTypeSize( Def->Type);
			memcpy( dstData.Data, Data, itemSize);
		}
	}
	else
	{
		if( Data_SString == Def->Type)
		{
			*(TArray<SString>*)dstData.Data = *(TArray<SString>*)Data;
		}
		else if( Data_DString == Def->Type)
		{
			*(TArray<DString>*)dstData.Data = *(TArray<DString>*)Data;
		}
		else if( Data_Struct_Ins == Def->Type)
		{
			_check( Def->StructType->ArrayCopyFunc);
			(*Def->StructType->ArrayCopyFunc)( dstData.Data, Data);
		}
		else if( Data_RefObj_Ptr == Def->Type)
		{
			if( Def->ClassType->IsTypeOf( Res::RTTI()))
			{
				*(TArray< P( Obj)>*)dstData.Data = (*(TArray< P( Obj) >*)Data);
			}
			else
			{
				TArray< P( Obj)>& dstArry = *(TArray< P( Obj)>*)dstData.Data;
				TArray< P( Obj)>& srcArry = *(TArray< P( Obj)>*)Data;

				dstArry.Clear( srcArry.Num());

				for( int i = 0 ; i < srcArry.Num(); ++i)
				{
					if( srcArry[i])
					{
						dstArry.Add( srcArry[i]->Clone());
					}
					else
					{
						dstArry.AddNew();
					}
				}
			}
		}
		else
		{
			//raw data
			s32_t itemSize = RawTypeSize( Def->Type);
			RawBuf& dstBuf = *(RawBuf*)dstData.Data;
			RawBuf& srcBuf = *(RawBuf*)Data;

			dstBuf.Clear( itemSize, srcBuf.Num());
			dstBuf.Add( srcBuf.Num(), itemSize);

			memcpy( dstBuf.Data(), srcBuf.Data(), itemSize*srcBuf.Num());
		}
	}

}

s32_t PropData::Num()
{
	if( !Def->IsArray)
	{
		return 1;
	}
	else
	{
		return (*(RawBuf*)Data).Num();
	}
}

void PropData::SetArrayNum( s32_t num)
{
	_check( Def->IsArray);

	if( Data_SString == Def->Type)
	{
		((TArray<SString>*)Data)->Resize( num);
	}
	else if( Data_DString == Def->Type)
	{
		((TArray<DString>*)Data)->Resize( num);
	}
	else if( Data_Struct_Ins == Def->Type)
	{
		_check( Def->StructType);
		_check( Def->StructType->ArrayResizeFunc);
		(*Def->StructType->ArrayResizeFunc)( Data, num);
	}
	else if( Data_RefObj_Ptr == Def->Type)
	{
		((TArray< P( Obj) >*)Data)->Resize( num);
	}
	else
	{
		s32_t itemSize = RawTypeSize( Def->Type);
		(*(RawBuf*)Data).Clear( itemSize, num);
		(*(RawBuf*)Data).Add( num, itemSize);
	}
}

void* PropData::operator[]( s32_t index)
{
	if( !Def->IsArray)
	{
		_check( index == 0);
		return Data;
	}
	else
	{
		s32_t itemSize = 0;
		if( Data_SString == Def->Type)
		{
			itemSize = sizeof( SString);
		}
		else if( Data_DString == Def->Type)
		{
			itemSize = sizeof( DString);
		}
		else if( Data_Struct_Ins == Def->Type)
		{
			_check( Def->StructType);
			itemSize = Def->StructType->CppSize;
		}
		else if( Data_RefObj_Ptr == Def->Type)
		{
			itemSize = sizeof( P( Obj));
		}
		else
		{
			itemSize = RawTypeSize( Def->Type);
		}

		return (void*)((byte_t*)(*(RawBuf*)Data).Data() + itemSize * index);
	}
}

////////////////////////////////////////////
StructDef::StructDef( const char* name, s32_t cppSize)
:Name( name)
,CppSize( cppSize)
,ArrayCopyFunc( 0)
,ArrayResizeFunc( 0)
{
}

PropDef* StructDef::GetProp( const char* name)
{
	for( s32_t i = 0 ; i < Props.Num(); ++i)
	{
		if( strcmp( Props[i]->Name ,name) ==0)
		{
			return Props[i];
		}
	}
	return 0;
}

void StructDef::_defProp( PropDef* def)
{
	def->Index = Props.Add( def);
	def->OwnerStruct = this;
}

bool StructDef::GetMemberData( void* stcPtr, PropDef* def, PropData& propData)
{
	_check( def->StructType == this);
	_check( Props.FindIndex(def) != null_index);

	propData.Data = (void*)((unsigned char*)stcPtr + def->Pos);
	propData.Def = def;

	return true;
}

void StructDef::CopyStruct( void* dstStrc, void* srcStrc)
{
	for( int i = 0 ; i < Props.Num(); ++i)
	{
		PropDef* propDef = Props[i];
		PropData dstData;
		GetMemberData( dstStrc, propDef, dstData);
		PropData srcData;
		GetMemberData( srcStrc, propDef, srcData);
		dstData.CopyTo( srcData);
	}

}

////////////////////////////////////////////
TMap< const char*, ClassDef*> ClassDef::sClsMap;

ClassDef::ClassDef( const char* name, ClassDef* super, Obj_CreateFunc createFunc)
:Name( name)
,SuperCls( super)
,CreateFunc(createFunc)
,PropIndexStart( -1)
,BindFuncIndexStart( -1)
{
}

ClassDef* ClassDef::GetClass( const char* clsName)
{
	ClassDef** ppDef = sClsMap.Find( clsName);
	if( ppDef == 0)
	{
		return 0;
	}

	return *ppDef;
}

bool ClassDef::IsTypeOf( ClassDef* testType)
{
	ClassDef* baseType = this;
	while( baseType)
	{
		if( baseType == testType)
		{
			return true;
		}

		baseType = baseType->SuperCls;
	}

	return false;
}

Obj* ClassDef::New()
{
	_check( CreateFunc);

	Obj* ret = (*CreateFunc)();

	return ret;
}

PropDef* ClassDef::GetProp( const char* name)
{
	TArray< PropDef*>::Iterator propIt( Props);

	while( propIt)
	{
		if( strcmp( (*propIt)->Name, name) == 0)
		{
			return (*propIt);
		}

		propIt.Next();
	}

	if( SuperCls)
	{
		return SuperCls->GetProp( name);
	}
	else
	{
		return 0;
	}
}

void ClassDef::_defProp( PropDef* propDef)
{
	_check( PropIndexStart != -1);
	propDef->Index = Props.Add( propDef) + PropIndexStart;
	propDef->OwnerCls = this;
}

void ClassDef::_bindFunc( FuncBind* bind)
{
	_check( PropIndexStart != -1);
	bind->Index = BindFuncs.Add( bind) + PropIndexStart;
}

void ClassDef::GetInheritClassList( TArray<ClassDef*>& list)
{
	if( SuperCls)
	{
		GetInheritClassList( list);
	}

	list.Add( this);
}

void ClassDef::Shutdown()
{
	sClsMap.Clear();
}
